package org.mineground.listener;

import java.net.InetAddress;

import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jibble.pircbot.Colors;
import org.mineground.Main;
import org.mineground.PermissionNodes;
import org.mineground.Utilities;
import org.mineground.commands.player.Login;
import org.mineground.handlers.IPBanHandler;
import org.mineground.handlers.NameBanHandler;
import org.mineground.player.MinegroundPlayer;
import org.mineground.player.PlayerJoinLogger;
import org.mineground.player.groupchat.GroupChat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.maxmind.geoip.Country;

/**
 * @file PlayerListener.java (22.01.2012)
 * @author Daniel Koenen
 *
 */
public class PlayerListener implements Listener {
	private static final Logger LOGGER = LoggerFactory.getLogger(PlayerListener.class);
	
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerLogin(final PlayerLoginEvent loginEvent) {
        Player eventPlayer = loginEvent.getPlayer();
        //String playerName = eventPlayer.getName();
        
        if (!loginEvent.getResult().equals(Result.ALLOWED) || eventPlayer == null) {
            loginEvent.disallow(Result.KICK_OTHER, "An unknown error occured, please reconnect.");
            return;
        }
        
        if (!isValidNickname(eventPlayer.getName())) {
            loginEvent.disallow(Result.KICK_OTHER, "Invalid nickname");
            return;
        }
        
        // loginEvent.getPlayer().getAddress() = null, so we need to get the player's IP from elsewhere.
        InetAddress playerAddress = loginEvent.getAddress();
        
        if (playerAddress != null) {
	        LOGGER.debug("Checking whether address '{}' is banned", playerAddress.getHostAddress());
	        
	        if (IPBanHandler.checkBan(playerAddress.getHostAddress(), loginEvent, eventPlayer)) {
	            return;
	        }
        }
/*      impersonating system is currently not working because of a bukkit issue, will re-do that later if possible.  
        if (Main.instance.containsPlayer(eventPlayer)) {
            PlayerLogManager.addLogEntry(PlayerLogManager.ACTION_ID_NOTE, playerName, "Server", playerAddress + "attempted to impersonate");
            IPBanHandler.addBan(playerAddress, "Impersonating " + playerName, new Date(0));
                
            StringBuilder informMessageBuilder = new StringBuilder();
            informMessageBuilder.append(Colors.RED);
            informMessageBuilder.append("* Notice: ");
            informMessageBuilder.append(Colors.BROWN);
            informMessageBuilder.append("A user connecting with ip-address ");
            informMessageBuilder.append(playerAddress);
            informMessageBuilder.append(" has attempted to impersonate ");
            informMessageBuilder.append(Colors.RED);
            informMessageBuilder.append(playerName);
            informMessageBuilder.append(Colors.BROWN);
            informMessageBuilder.append(". The user has been banned permanently.");
            Main.instance.ircHandler.sendMessage(Main.instance.configHandler.ircDevChannel, informMessageBuilder.toString());
            
            loginEvent.disallow(Result.KICK_OTHER, "Impersonating " + playerName);
            return;
        }
*/
        MinegroundPlayer playerInstance = new MinegroundPlayer(eventPlayer);
        playerInstance.initializePlayer();
        Main.getInstance().addPlayer(eventPlayer, playerInstance);
        
        if (NameBanHandler.checkBan(loginEvent, playerInstance)) {
            return;
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerJoin(final PlayerJoinEvent joinEvent) {
        Player eventPlayer = joinEvent.getPlayer();
       
        PlayerJoinLogger.addLog(joinEvent);
        MinegroundPlayer playerInstance = Main.getInstance().getPlayer(eventPlayer);
        
        eventPlayer.sendMessage(ChatColor.LIGHT_PURPLE + "Welcome to Las Venturas Mineground!");
        
        if (!playerInstance.isRegistered()) {
            eventPlayer.sendMessage(ChatColor.LIGHT_PURPLE + "You must register at www.Mineground.com/register to build!");
        }
        
        else {
            playerInstance.loadPlayer();
            playerInstance.isLoggedIn();
        }

        String playerAddress = eventPlayer.getAddress().getAddress().getHostAddress();
        Country playerCountry = Main.getInstance().getGeoIPService().getCountry(playerAddress);
        joinEvent.setJoinMessage(ChatColor.AQUA + eventPlayer.getName() + ChatColor.GOLD + " joined Las Venturas Mineground.");
        Main.getInstance().getIRCHandler().sendEchoMessage(Colors.DARK_GREEN + eventPlayer.getName() + " joined Las Venturas Mineground");
        Main.getInstance().getIRCHandler().sendMessage("&" + Main.getInstance().getConfigHandler().getIrcChannel(), Colors.BLUE + "IP: " + playerAddress + " / Location: " + playerCountry.getName());
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerQuit(final PlayerQuitEvent quitEvent) {
        Player eventPlayer = quitEvent.getPlayer();
        
        if (Main.getInstance().containsPlayer(eventPlayer)) {
            MinegroundPlayer playerInstance = Main.getInstance().getPlayer(eventPlayer);
            
            if (playerInstance.isKicked()) {
                quitEvent.setQuitMessage(ChatColor.AQUA + eventPlayer.getName() + ChatColor.RED + " has been kicked from Las Venturas Mineground.");
                Main.getInstance().getIRCHandler().sendEchoMessage(Colors.DARK_GREEN + eventPlayer.getName() + " has been kicked from Las Venturas Mineground");
                removeFromChatGroup(playerInstance);
                Main.getInstance().removePlayer(eventPlayer);
                return;
            }
            
            if (playerInstance.isLoggedIn()) {
                playerInstance.savePlayer();
            }
            
            removeFromChatGroup(playerInstance);
            Main.getInstance().removePlayer(eventPlayer);
        }
        
        quitEvent.setQuitMessage(ChatColor.AQUA + eventPlayer.getName() + ChatColor.RED + " left Las Venturas Mineground.");
        Main.getInstance().getIRCHandler().sendEchoMessage(Colors.DARK_GREEN + eventPlayer.getName() + " left Las Venturas Mineground");
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerChat(final AsyncPlayerChatEvent chatEvent) {
        if (chatEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = chatEvent.getPlayer();
        String chatMessage = chatEvent.getMessage();
        
        if (!isLoggedIn(eventPlayer)) {
           chatEvent.setCancelled(true);
           return;
        }
        
        MinegroundPlayer playerInstance = Main.getInstance().getPlayer(eventPlayer);
        
        if (playerInstance == null) {
            eventPlayer.sendMessage("Sorry, an unknown error occured, please reconnect.");
            chatEvent.setCancelled(true);
            return;
        }
        
        if (playerInstance.isMuted() != MinegroundPlayer.MUTED_NOTMUTED) {
            int muteTime = playerInstance.getMuteTime();
            
            if (muteTime == -1) {
                eventPlayer.sendMessage(ChatColor.RED + "* Error: You have been permanently muted and are not able to use the chat anymore.");
            }
            
            else {
                double difference = (System.currentTimeMillis() - playerInstance.getMuteTick()) / 60000;
                
                if (difference >= muteTime) {
                    playerInstance.unmutePlayer();
                    return;
                }
                
                eventPlayer.sendMessage(ChatColor.RED + "* Error: You have been muted for " + muteTime + " minutes and are not able to use the chat for " + Math.round(muteTime - difference) + " minutes.");
                chatEvent.setCancelled(true);
                return;
            }
        }
        
        if (chatMessage.startsWith(Main.getInstance().getConfigHandler().getAdminChatPrefix()) && eventPlayer.hasPermission(PermissionNodes.PERMISSION_USE_ADMIN_CHAT)) {
            for (Player loopPlayer : Main.getInstance().getServer().getOnlinePlayers()) {
                if (!loopPlayer.hasPermission(PermissionNodes.PERMISSION_READ_ADMIN_CHAT) || !isLoggedIn(loopPlayer, false)) {
                    continue;
                }
                
                loopPlayer.sendMessage(ChatColor.GOLD + "Admin " + eventPlayer.getDisplayName() + ChatColor.GOLD + ": " + chatMessage.substring(1));
            }
            
            Main.getInstance().getIRCHandler().sendAdminMessage(Colors.DARK_BLUE + "*** " + Colors.OLIVE + "Admin " + Utilities.fixName(eventPlayer) + ":" + Colors.NORMAL + " " + chatMessage.substring(1));
            chatEvent.setCancelled(true);
            return;
        }
        
        int groupChatId = Main.getInstance().getPlayer(eventPlayer).getGroupChatId();
        if (chatMessage.startsWith(Main.getInstance().getConfigHandler().getGroupChatPrefix()) && groupChatId != -1) {
            for (Player player : Main.getInstance().getGroupChatHandler().getGroupChat(groupChatId).getGroupPlayers()) {
                player.sendMessage(ChatColor.AQUA + "[" + Main.getInstance().getGroupChatHandler().getGroupChat(groupChatId).getGroupName() + "] " + eventPlayer.getDisplayName() + ": " + chatMessage.substring(1));
            }
            
            Main.getInstance().getIRCHandler().sendAdminMessage(Colors.DARK_BLUE + "*** " + Colors.OLIVE + Main.getInstance().getGroupChatHandler().getGroupChat(groupChatId).getGroupName() + " " + Utilities.fixName(eventPlayer) + ": " + Colors.NORMAL + chatMessage.substring(1));
            chatEvent.setCancelled(true);
            return;
        }
        
        Main.getInstance().getReactionTestHandler().checkText(chatEvent);
        Main.getInstance().getIRCHandler().sendEchoMessage(Colors.OLIVE + Utilities.fixName(eventPlayer) + Colors.NORMAL + ": " + chatMessage);
    }

/*  impersonating system is currently not working because of a bukkit issue, will re-do that later if possible.
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerKick(final PlayerKickEvent kickEvent) {
        if (kickEvent.isCancelled()) {
            return;
        }
        System.out.println("test");
        if (kickEvent.getReason().equals("Logged in from another location.")) {
            kickEvent.setCancelled(true);
            return;
        }
        
        MinegroundPlayer playerInstance = Main.instance.getPlayer(kickEvent.getPlayer());
        
        if (playerInstance == null) {
            return;
        }
        
        if (playerInstance.isLoggedIn()) {
            playerInstance.savePlayer();
        }
            
        removeFromChatGroup(playerInstance);
        Main.instance.removePlayer(kickEvent.getPlayer());
    }
*/
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerChangedWorld(final PlayerChangedWorldEvent changedWorldEvent) {
        Player eventPlayer = changedWorldEvent.getPlayer();
        
        if (eventPlayer.getWorld() == Main.getInstance().getServer().getWorld(Main.getInstance().getConfigHandler().getCreativeWorldName())) {
            eventPlayer.setGameMode(GameMode.CREATIVE);
            return;
        }
        eventPlayer.setGameMode(GameMode.SURVIVAL);
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerRespawn(final PlayerRespawnEvent respawnEvent) {
        int noDamageTicks = 40;
        Player eventPlayer = respawnEvent.getPlayer();
        
        eventPlayer.setNoDamageTicks(noDamageTicks);
        if (!Main.getInstance().containsPlayer(eventPlayer)) {
            return;
        }
        
        if (Main.getInstance().getPlayer(eventPlayer).getDieWorld() != null) {
            respawnEvent.setRespawnLocation(Main.getInstance().getPlayer(eventPlayer).getDieWorld().getSpawnLocation());
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent commandPreprocessEvent) {
        if (commandPreprocessEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = commandPreprocessEvent.getPlayer();
        String[] splittedMessage = commandPreprocessEvent.getMessage().split(" ");
        
        if (!splittedMessage[0].equalsIgnoreCase("/login") && !isLoggedIn(eventPlayer)) {
            commandPreprocessEvent.setCancelled(true);
            return;
        }
        
        if (splittedMessage[0].equalsIgnoreCase("/me")) {
            String commandSender = Utilities.fixName(commandPreprocessEvent.getPlayer());
            
            String commandMessage;
            StringBuilder commandMessageBuilder = new StringBuilder();

            for (int index = 1; index < splittedMessage.length; index++) {
                commandMessageBuilder.append(splittedMessage[index]);
                commandMessageBuilder.append(" ");
            }
            
            commandMessage = commandMessageBuilder.toString().substring(0, commandMessageBuilder.toString().length() - 1);
            Main.getInstance().getIRCHandler().sendEchoMessage(Colors.OLIVE + commandSender + Colors.OLIVE + " " + commandMessage);
        }
        
        
        // Temporary handling for /login not logging in the server.log
        if (splittedMessage[0].equalsIgnoreCase("/login")) {
            String[] commandArguments = new String[splittedMessage.length - 1];
            Login playerLoginCommand = new Login();
            System.arraycopy(splittedMessage, 1, commandArguments, 0, splittedMessage.length - 1);
            playerLoginCommand.triggerCommandExecution(eventPlayer, splittedMessage[0], commandArguments);
            commandPreprocessEvent.setCancelled(true);
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerMove(final PlayerMoveEvent moveEvent) {
        if (moveEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = moveEvent.getPlayer();
        
        if (!isLoggedIn(eventPlayer)) {
            eventPlayer.teleport(moveEvent.getFrom());
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerEnterPortal(final PlayerPortalEvent portalEvent) {
        if (portalEvent.isCancelled()) {
            return;
        }

        Player eventPlayer = portalEvent.getPlayer();
        if (eventPlayer.getWorld().getName().equals(Main.getInstance().getConfigHandler().getSurvivalWorldName())) {
            eventPlayer.teleport(Main.getInstance().getServer().getWorld(Main.getInstance().getConfigHandler().getSurvivalNetherWorldName()).getSpawnLocation());
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerDropItem(final PlayerDropItemEvent dropEvent) {
        if (dropEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = (Player) dropEvent.getPlayer();
        
        if (!isLoggedIn(eventPlayer)) {
            dropEvent.setCancelled(true);
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onInventoryOpen(final InventoryOpenEvent openEvent) {
        if (openEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = (Player) openEvent.getPlayer();
        
        if (!isLoggedIn(eventPlayer)) {
            openEvent.setCancelled(true);
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerInteract(PlayerInteractEvent interactEvent) {
        if(interactEvent.isCancelled()) {
            return;
        }
        
        Player eventPlayer = interactEvent.getPlayer();
        
        if (!isLoggedIn(eventPlayer)) {
            interactEvent.setCancelled(true);
        }
        
        if (eventPlayer.getWorld().getName().equalsIgnoreCase(Main.getInstance().getConfigHandler().getCreativeWorldName()) && !eventPlayer.hasPermission(PermissionNodes.PERMISSION_ALLOW_CREATIVE_ENTITY_SPAWN)) {
            if (isEntitySpawnerEgg(interactEvent.getMaterial())) {
                eventPlayer.sendMessage(ChatColor.RED + "* You are not allowed to use eggs in the creative world.");
                interactEvent.setCancelled(true);
            }
        }
    }
    
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerTeleport(final PlayerTeleportEvent teleportEvent) {
        int noDamageTicks = 40;
        Player eventPlayer = teleportEvent.getPlayer();
        eventPlayer.setNoDamageTicks(noDamageTicks);
    }
   
    private boolean isValidNickname(String playerName) {
        if (!playerName.matches("^[a-zA-Z0-9_]{2,20}$")) {
            return false;
        }
 
        return true;
    }
    
    private boolean isLoggedIn(Player playerToCheck) {
        return isLoggedIn(playerToCheck, true);
    }
    
    private boolean isLoggedIn(Player playerToCheck, boolean shouldSendMessage) {
        MinegroundPlayer playerInstance = Main.getInstance().getPlayer(playerToCheck);
        if (playerInstance != null && !playerInstance.isLoggedIn() && playerInstance.isRegistered()) {
            if (shouldSendMessage) {
                playerToCheck.sendMessage(ChatColor.RED + "* Please '/login' first!");
            }
            
            return false;
        }
        
        return true;
    }
    
    private boolean isLoggedInAndRegistered(Player playerToCheck) {
        MinegroundPlayer playerInstance = Main.getInstance().getPlayer(playerToCheck);
        if (playerInstance != null && !playerInstance.isLoggedIn() && !playerInstance.isRegistered()) {
            playerToCheck.sendMessage(ChatColor.RED + "* Please go to www.mineground.com and register your name first!");
            return false;
        }
        
        return true;
    }
    
    private void removeFromChatGroup(MinegroundPlayer playerInstance) {
        int groupId = playerInstance.getGroupChatId();
        GroupChat groupChat = Main.getInstance().getGroupChatHandler().getGroupChat(groupId);
        
        if (groupId != -1 && groupChat != null) {
            Main.getInstance().getGroupChatHandler().getGroupChat(groupId).removePlayer(playerInstance.getPlayer());
        }
    }
    
    private boolean isEntitySpawnerEgg(Material material) {
        if (material.getId() == 383 || material.getId() == 344) {
            return true;
        }
        
        return false;
    }
}
